using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using MainGame.GameSound;


namespace MainGame.GamePlay
{
  public struct CardAnimation
  {
    public AnimationType AnimationType;
    public Card Card;
    public int PlayerIndex;
    public bool AnimationFinished;
    public Vector2 Destination;
    public int Amount;
    public float RotateToAngle;
    public float AnimationTimer;
  }

  public enum AnimationType
  {
    MoveTo,
    MoveToPlayer,
    MoveToTable,
    MoveByAmount,
    CardRotate,
    DistributeCards,
    Scopa,
    SpaceCards,
    Turn,
    ShowPlayerCards,
    ShowTakeCard
  }

  public abstract class AnimationRenderer : BoardRenderer
  {
    private static readonly int MOVESPEED = 50;
    private static readonly int TURNSPEED = 30;
    private static readonly float ROTATESPEED = 0.2f;
    private float rotateBy = ROTATESPEED;
    private float scopaTimer = 0;
    private Texture2D logo;


    public AnimationRenderer(Game game)
      : base(game)
    {
    }

    public override void Initialize()
    {
      base.Initialize();
    }

    protected override void LoadContent()
    {
      logo = Game.Content.Load<Texture2D>(@"Images//Menu//scopalone1");
      base.LoadContent();
    }

    public override void Update(GameTime gameTime)
    {
      base.Update(gameTime);
    }

    public override void UpdateAnimation(GameTime gameTime)
    {
      // Check if there are animations to start
      if (gameState.MossaFatta)
      {
        // Check if it's necessary to adjust the cards' properties
        for (int i = 0; i < gameState.Animations.Count; i++)
        {
          var anim = gameState.Animations[i];
          // If animation type is MoveToTable, adjust card visualization
          if (anim.AnimationType == AnimationType.MoveToTable)
            gameState.Animations[i] = StartMoveToTable(anim);

          // If animation type is MoveByAmount, show the front of the card
          if (anim.AnimationType == AnimationType.MoveByAmount)
          {
            gameState.Animations[i] = StartMoveByAmount(anim);
            anim.Card.ShowBack = false;
            //soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
          }

          // If animation type is ShowTakeCard, also show the shadow of the card
          if (anim.AnimationType == AnimationType.ShowTakeCard)
          {
            gameState.Animations[i] = StartMoveByAmount(anim);
            anim.Card.ShowBack = false;
            anim.Card.IsBeingDragged = true;
            soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
          }

          // If animation type is ShowPlayerCards, initialize MoveByAmount
          if (anim.AnimationType == AnimationType.ShowPlayerCards)
            gameState.Animations[i] = StartMoveByAmount(anim);

          // If animation type includes rotating a card, extract a random rotation speed
          if (anim.AnimationType == AnimationType.CardRotate || anim.AnimationType == AnimationType.MoveToTable)
          {
            StartRotate();
            soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
          }

          // If animation type is MoveToPlayer, set depth of the cards in the take
          if (anim.AnimationType == AnimationType.MoveToPlayer)
            anim.Card.Depth = FLOATING_CARD_DEPTH;

          // If animation type is Scopa, start a sound and set the timer
          if (anim.AnimationType == AnimationType.Scopa)
          {
            scopaTimer = 1.0f;
            soundManager.PlaySound(GameSound.GamePlaySounds.Scopa);
          }
        }


        gameState.MossaAnimata = false;
        gameState.MossaFatta = false;
      }

      if (!gameState.MossaAnimata)
      {
        // Play animations
        for (int i = 0; i < gameState.Animations.Count; i++)
        {
          var animation = gameState.Animations[i];
          var animationFinished = animation.AnimationFinished;
          if (animation.AnimationFinished == true)
            continue;

          switch (animation.AnimationType)
          {
            case AnimationType.Turn:
              animation.AnimationFinished = UpdateTurn(animation.Card);
              break;
            case AnimationType.MoveToPlayer:
              animation.AnimationFinished = UpdateMoveToPlayer(animation.Card, animation.PlayerIndex);
              break;
            case AnimationType.MoveToTable:
              UpdateCardRotate(animation.Card, animation.Card.Rotation + 1.0f);
              animation.AnimationFinished = UpdateMoveToTable(animation.Card);
              break;
            case AnimationType.MoveByAmount:
              //animation.AnimationFinished = UpdateMoveTo(animation.Card, new Vector2(animation.Card.CoordPosition.X, playerAreas[animation.PlayerIndex].blockPosition.Y + (playerAreas[animation.PlayerIndex].height / 2)));
              animation = UpdateMoveByAmount(animation, gameTime);
              break;
            case AnimationType.CardRotate:
              animation.AnimationFinished = UpdateCardRotate(animation.Card, animation.RotateToAngle);
              break;
            case AnimationType.Scopa:
              animation.AnimationFinished = UpdateScopa();
              break;
            case AnimationType.DistributeCards:
              animation.AnimationFinished = StartDistribute();
              break;
            case AnimationType.MoveTo:
              animation.AnimationFinished = UpdateMoveTo(animation.Card, animation.Destination);
              break;
            case AnimationType.ShowPlayerCards:
              UpdateMoveByAmount(animation, gameTime);
              animation.AnimationFinished = UpdateTurn(animation.Card);
              break;
            case AnimationType.ShowTakeCard:
              animation = UpdateMoveByAmount(animation, gameTime);
              break;
          }

          gameState.Animations[i] = animation;

        }

        if (gameState.Animations.All(a => a.AnimationFinished))
        {
          gameState.MossaAnimata = true;
          gameState.Animations.Clear();
          //renderState = RenderState.Input;
        }
      }
    }


    private bool ShowPlayerCards(CardAnimation animation, GameTime gameTime)
    {
      bool moveFinished = false, turnFinished = false;

      moveFinished = UpdateMoveByAmount(animation, gameTime).AnimationFinished;
      turnFinished = UpdateTurn(animation.Card);

      if (moveFinished && turnFinished)
        return true;
      else
        return false;
    }

    private void StartRotate()
    {
      rotateBy = random.Next(1, 3) * 0.1f;
      var sign = random.Next(100);
      if ((sign % 2) != 0)
        rotateBy = -rotateBy;
    }

    private bool UpdateCardRotate(Card card, float rotation)
    {
      if (rotation - card.Rotation < rotateBy)
      {
        card.Rotation = rotation % MathHelper.TwoPi;
        return true;
      }
      card.Rotation += rotateBy;
      return false;
    }

    private bool StartDistribute()
    {
      for (int i = 0; i < gameState.Players.Count; i++)
      {
        var player = gameState.Players[i];

        for (int j = 0; j < player.Cards.Count; j++)
        {
          var card = player.Cards[j];
          card.ShowBack = true;
          var destination = playerAreas[i].slotCoords[j];
          if ((player.AIType == AIType.Human) && (gameState.Players.Count(p => p.AIType == AIType.Human) == 1))
            card.ShowBack = false;
          card.CoordPosition = player.Position;
          card.Rotation = playerAreas[i].cardsRotation;
          gameState.Animations.Add(new CardAnimation() { AnimationType = AnimationType.MoveTo, Destination = destination, Card = card, AnimationFinished = false });
        }
      }
      soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));
      soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));
      soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));
      return true;
    }

    private CardAnimation StartMoveToTable(CardAnimation animation)
    {
      // Check if it's necessary to rotate the card
      var boundsCenter = new Vector2(gameBounds.Center.X, gameBounds.Center.Y);
      Vector2 playerToCenter = Vector2.Subtract(boundsCenter, gameState.Players[animation.PlayerIndex].Position);
      playerToCenter.Normalize();

      // Move horizontally
      if (playerToCenter.X > 0)
        // Move right
        animation.RotateToAngle = MathHelper.ToRadians(180.0f);
      if (playerToCenter.X < 0)
        // Move left
        animation.RotateToAngle = MathHelper.ToRadians(360.0f);

      // Check if the card shows its back
      if (animation.Card.ShowBack)
        animation.Card.ShowBack = false;

      // Adjust the card depth
      //animation.Card.Depth = TABLE_CARD_DEPTH + 0.05f;
      animation.Card.Depth = FLOATING_CARD_DEPTH;

      return animation;
    }

    private bool UpdateMoveToTable(Card card)
    {
      // The card will move straight to the table
      var playerPosition = gameState.Players.ElementAt(gameState.CurrentPlayerIndex).Position;
      Vector2 toTableCenter = Vector2.Subtract(new Vector2(gameBounds.Center.X, gameBounds.Center.Y), playerPosition);
      //var destination = toTableCenter;
      return UpdateMoveTo(card, new Vector2(gameBounds.Center.X, gameBounds.Center.Y));
    }

    private CardAnimation StartMoveByAmount(CardAnimation animation)
    {
      var card = animation.Card;
      var boundsCenter = new Vector2(gameBounds.Center.X, gameBounds.Center.Y);
      Vector2 playerToCenter = Vector2.Subtract(boundsCenter, gameState.Players[animation.PlayerIndex].Position);
      playerToCenter.Normalize();
      Vector2 destination = boundsCenter;

      // Move horizontally
      if (playerToCenter.X > 0)
        // Move right
        destination = new Vector2(card.CoordPosition.X + animation.Amount, card.CoordPosition.Y);
      if (playerToCenter.X < 0)
        // Move left
        destination = new Vector2(card.CoordPosition.X - animation.Amount, card.CoordPosition.Y);
      // Move vertically
      if (playerToCenter.Y > 0)
        // Move down
        destination = new Vector2(card.CoordPosition.X, card.CoordPosition.Y + animation.Amount);
      if (playerToCenter.Y < 0)
        // Move up
        destination = new Vector2(card.CoordPosition.X, card.CoordPosition.Y - animation.Amount);

      // Adjust the card depth
      animation.Card.Depth += 0.06f;

      animation.Destination = destination;
      animation.AnimationTimer = 0.8f;

      return animation;
    }

    private CardAnimation UpdateMoveByAmount(CardAnimation animation, GameTime gameTime)
    {
      animation.AnimationTimer -= gameTime.dt();
      if (animation.AnimationTimer > 0f)
      {
        // Move a card down by an amount
        animation.Amount -= MOVESPEED;
        UpdateMoveTo(animation.Card, animation.Destination);
      }
      else
        animation.AnimationFinished = true;

      return animation;
    }

    private bool UpdateMoveToPlayer(Card card, int playerIndex)
    {
      // Find the destination
      var destination = gameState.Players.ElementAt(playerIndex).Position;
      return UpdateMoveTo(card, destination);
    }


    private bool UpdateScopa()
    {
      if (scopaTimer <= 0.0f)
      {
        gameState.MossaAnimata = true;
        return true;
      }
      return false;
    }
    

    private bool UpdateMoveTo(Card c, Vector2 destination)
    {
      Vector2 distanceVector = Vector2.Subtract(destination, c.CoordPosition);
      if (distanceVector.Length() < MOVESPEED)
      {
        c.CoordPosition = destination;
        return true;
      }
      Vector2 dx = Vector2.Normalize(distanceVector) * MOVESPEED;
      c.CoordPosition += dx;
      return false;
    }

    private bool UpdateTurn(Card c)
    {
      if (c.Width < TURNSPEED)
      {
        //c.CoordPosition.X -= CARD_WIDTH / 2;
        c.Width = CARD_WIDTH;
        c.ShowBack = !c.ShowBack;
        c.IsTurning = false;
        return true;
      }
      //c.CoordPosition.X += TURNSPEED / 2;
      c.Width -= TURNSPEED;
      c.IsTurning = true;
      return false;
    }

    public override void Draw(GameTime gameTime)
    {
      base.Draw(gameTime);
    }

    public override void DrawAnimation(GameTime gameTime)
    {

    }

    private int Scopaw = 0;
    private Rectangle scopaRect = new Rectangle(500, 240, 540, 366);
    public override void DrawScopa(GameTime gameTime)
    {
      if (scopaTimer <= 0.0f)
      {
        scopaRect = new Rectangle(600, 300, 540, 366);
        Scopaw = 0;
      }

      if (scopaTimer > 0.0f)
      {
        Scopaw +=2;

        if (Scopaw > 20) Scopaw *= -1;

        scopaTimer -= gameTime.dt();

        scopaRect.Width += Scopaw;
        scopaRect.Height += Scopaw;

        spriteBatch.Draw(logo, scopaRect, null, Color.White, 0, new Vector2(400, 240), SpriteEffects.None, 1.0f);
      }
    }

  }
}
